﻿/*
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
 *
 * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include "JT1078.h"
#include "Util/util.h"
#include "Rtsp/Rtsp.h"

using namespace toolkit;

namespace mediakit {

static uint64_t htonll_c(uint64_t val) {
    return (((uint64_t) htonl(val)) << 32) + htonl(val >> 32);
}

static uint64_t ntohll_c(uint64_t val){
    return (((uint64_t) ntohl(val)) << 32) + ntohl(val >> 32);
}

#ifndef htonll
#define htonll htonll_c
#endif

#ifndef ntohll
#define ntohll ntohll_c
#endif

const char *getFrameTypeName(JT1078FrameType type) {
    switch (type) {
#define XX(name, value) case JT1078FrameType::name: return #name;
        JT1078_FRAME_MAP(XX)
#undef XX
        default: return "invalid jt1078 frame type";
    }
}

const char *getSliceTypeName(JT1078SliceType type) {
    switch (type) {
#define XX(name, value) case JT1078SliceType::name: return #name;
        JT1078_SLICE_MAP(XX)
#undef XX
        default: return "invalid jt1078 slice type";
    }
}

uint64_t JT1078Payload::getStampMS(JT1078FrameType type) const {
    switch (type) {
        case JT1078FrameType::B_FRAME:
        case JT1078FrameType::P_FRAME:
        case JT1078FrameType::I_FRAME: return ntohll(video.stamp);
        case JT1078FrameType::AUDIO_FRAME: return ntohll(audio.stamp);
        default: return 0;
    }
}

uint16_t JT1078Payload::size(JT1078FrameType type) const {
    switch (type) {
        case JT1078FrameType::B_FRAME:
        case JT1078FrameType::P_FRAME:
        case JT1078FrameType::I_FRAME: return ntohs(video.size);
        case JT1078FrameType::AUDIO_FRAME: return ntohs(audio.size);
        case JT1078FrameType::RAW_FRAME: return ntohs(raw.size);
        default: return 0;
    }
}

const uint8_t* JT1078Payload::data(JT1078FrameType type) const{
    switch (type) {
        case JT1078FrameType::B_FRAME:
        case JT1078FrameType::P_FRAME:
        case JT1078FrameType::I_FRAME: return video.data;
        case JT1078FrameType::AUDIO_FRAME: return audio.data;
        case JT1078FrameType::RAW_FRAME: return raw.data;
        default: return nullptr;
    }
}

uint16_t JT1078Payload::getLastKeyFrameInterval(JT1078FrameType type) const {
    switch (type) {
        case JT1078FrameType::B_FRAME:
        case JT1078FrameType::P_FRAME:
        case JT1078FrameType::I_FRAME: return ntohs(video.last_key_frame_interval);
        default: return 0;
    }
}

uint16_t JT1078Payload::getLastFrameInterval(JT1078FrameType type) const {
    switch (type) {
        case JT1078FrameType::B_FRAME:
        case JT1078FrameType::P_FRAME:
        case JT1078FrameType::I_FRAME: return ntohs(video.last_frame_interval);
        default: return 0;
    }
}

std::string JT1078Payload::dumpString(JT1078FrameType type) const{
    return StrPrinter << "frame_type:" << getFrameTypeName(type) << "\n"
                      << "timestamp:" << getStampMS(type) << "\n"
                      << "data_len:" << size(type) << "\n"
                      << "last_key_frame_interval:" << getLastKeyFrameInterval(type) << "\n"
                      << "last_frame_interval:" << getLastFrameInterval(type);
}

size_t JT1078Payload::getMinSize(JT1078FrameType type) {
    switch (type) {
        case JT1078FrameType::B_FRAME:
        case JT1078FrameType::P_FRAME:
        case JT1078FrameType::I_FRAME: return sizeof(video);
        case JT1078FrameType::AUDIO_FRAME: return sizeof(audio);
        case JT1078FrameType::RAW_FRAME: return sizeof(raw);
        default: throw std::invalid_argument("JT1078 frame type not supported");
    }
}

bool isJT1078(const char *data, size_t len) {
    if (len < 4) {
        return false;
    }
    return data[0] == 0x30 && data[1] == 0x31 && data[2] == 0x63 && data[3] == 0x64;
}

const char* getJT1078Tail(const char *data, size_t len, bool check_prefix){
    if (check_prefix && !isJT1078(data, len)) {
        return nullptr;
    }
    data += kJT1078_MAGIC_SIZE;
    len -= kJT1078_MAGIC_SIZE;

    if (len < RtpPacket::kRtpHeaderSize) {
        // 数据不够
        return nullptr;
    }

    auto rtp = (RtpHeader *)(data);
    auto type = (JT1078FrameType)rtp->frame_type;
    if (len < RtpPacket::kRtpHeaderSize + JT1078Payload::getMinSize(type)) {
        // 数据不够
        return nullptr;
    }

    // jt1078 csrc固定为1，不适用于 RtpPacket::getPayloadData()
    auto payload = (JT1078Payload *)rtp->payload;
    auto next_rtp = (const char*)payload->data(type) + payload->size(type);
    if (next_rtp > data + len) {
        // 数据不够
        return nullptr;
    }
    return next_rtp;
}

} //mediakit